package com.dimples.dd.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.nacos.shaded.com.google.common.annotations.VisibleForTesting;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dimples.dd.common.constant.DDConstant;
import com.dimples.dd.common.enums.CommonStatusEnum;
import com.dimples.dd.common.model.Option;
import com.dimples.dd.system.convert.OrgConverter;
import com.dimples.dd.system.mapper.SystemOrgMapper;
import com.dimples.dd.system.model.bo.OrgBO;
import com.dimples.dd.system.model.entity.SystemOrgDO;
import com.dimples.dd.system.model.form.OrgForm;
import com.dimples.dd.system.model.query.OrgPageQuery;
import com.dimples.dd.system.model.vo.OrgTreeVO;
import com.dimples.dd.system.service.SystemOrgService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static com.dimples.dd.common.exception.BizExceptionUtil.exception;
import static com.dimples.dd.common.exception.ErrorCode.*;

@Service
@RequiredArgsConstructor
public class SystemOrgServiceImpl extends ServiceImpl<SystemOrgMapper, SystemOrgDO> implements SystemOrgService {

    private final OrgConverter orgConverter;

    @Override
    public List<Option<String>> listOrgOptions() {
        List<SystemOrgDO> deptList = this.list(
                new LambdaQueryWrapper<SystemOrgDO>()
                        .select(SystemOrgDO::getOrgId, SystemOrgDO::getOrgName, SystemOrgDO::getParentId)
                        .eq(SystemOrgDO::getStatus, CommonStatusEnum.ENABLE.getValue())
                        .orderByAsc(SystemOrgDO::getOrderNum)
        );

        Set<String> parentIds = deptList.stream().map(SystemOrgDO::getParentId).collect(Collectors.toSet());
        Set<String> orgIds = deptList.stream().map(SystemOrgDO::getOrgId).collect(Collectors.toSet());
        List<String> rootIds = CollectionUtil.subtractToList(parentIds, orgIds);

        List<Option<String>> list = CollUtil.newArrayList();
        for (String rootId : rootIds) {
            list.addAll(recurOrgTreeOptions(rootId, deptList));
        }
        // 添加顶层父级, id为0
        Option<String> rootOption = new Option<>(DDConstant.ROOT_NODE_ID_STR, "顶级机构");
        rootOption.setChildren(list);
        return CollUtil.newArrayList(rootOption);
    }

    /**
     * 递归获取机构树选项
     *
     * @param parentId 父级ID
     * @param orgList  机构列表
     * @return 机构下拉列表
     */
    public static List<Option<String>> recurOrgTreeOptions(String parentId, List<SystemOrgDO> orgList) {
        return CollectionUtil.emptyIfNull(orgList).stream()
                .filter(orgDO -> orgDO.getParentId().equals(parentId))
                .map(orgDO -> {
                    Option<String> option = new Option<>(orgDO.getOrgId(), orgDO.getOrgName());
                    List<Option<String>> children = recurOrgTreeOptions(orgDO.getOrgId(), orgList);
                    if (CollectionUtil.isNotEmpty(children)) {
                        option.setChildren(children);
                    }
                    return option;
                })
                .collect(Collectors.toList());
    }

    @Override
    public List<OrgTreeVO> listOrgs(OrgPageQuery queryParams) {
        List<OrgBO> deptList = this.baseMapper.selectList(queryParams);

        Set<String> deptIds = deptList.stream().map(OrgBO::getOrgId).collect(Collectors.toSet());
        Set<String> parentIds = deptList.stream().map(OrgBO::getParentId).collect(Collectors.toSet());
        List<String> rootIds = CollUtil.subtractToList(parentIds, deptIds);

        List<OrgTreeVO> list = CollUtil.newArrayList();
        for (String rootId : rootIds) {
            list.addAll(recurDeptList(rootId, deptList));
        }
        return list;
    }

    /**
     * 递归获取部门列表
     *
     * @param parentId 父节点
     * @param orgList  列表数据
     * @return 树结构数据
     */
    public List<OrgTreeVO> recurDeptList(String parentId, List<OrgBO> orgList) {
        return orgList.stream()
                .filter(orgBO -> orgBO.getParentId().equals(parentId))
                .map(orgBO -> {
                    OrgTreeVO deptVO = orgConverter.entity2Vo(orgBO);
                    List<OrgTreeVO> children = recurDeptList(orgBO.getOrgId(), orgList);
                    deptVO.setChildren(children);
                    return deptVO;
                }).collect(Collectors.toList());
    }

    @Override
    public OrgForm selectOrgForm(String orgId) {
        SystemOrgDO entity = this.getOne(new LambdaQueryWrapper<SystemOrgDO>()
                .eq(SystemOrgDO::getOrgId, orgId));

        return orgConverter.entity2Form(entity);
    }

    @Override
    public String createOrg(OrgForm formData) {
        if (formData.getParentId() == null) {
            formData.setParentId(DDConstant.ROOT_NODE_ID_STR);
        }
        // 校验父部门的有效性
        validateParentOrg(null, formData.getParentId());
        // 校验部门名的唯一性
        validateOrgNameUnique(null, formData.getParentId(), formData.getOrgName());

        SystemOrgDO entity = orgConverter.form2Entity(formData);
        // 部门路径
        String treePath = generateOrgTreePath(formData.getParentId());
        entity.setParentIds(treePath);
        // 保存部门并返回部门ID
        this.save(entity);
        return entity.getOrgId();
    }

    @Override
    public String updateOrg(String orgId, OrgForm formData) {
        if (formData.getParentId() == null) {
            formData.setParentId(DDConstant.ROOT_NODE_ID_STR);
        }
        // 校验自己存在
        validateOrgExists(formData.getOrgId());
        // 校验父部门的有效性
        validateParentOrg(formData.getOrgId(), formData.getParentId());
        // 校验部门名的唯一性
        validateOrgNameUnique(formData.getOrgId(), formData.getParentId(), formData.getOrgName());

        // form->entity
        SystemOrgDO entity = orgConverter.form2Entity(formData);
        entity.setOrgId(orgId);
        // 部门路径
        String treePath = generateOrgTreePath(formData.getParentId());
        entity.setParentIds(treePath);
        // 保存部门并返回部门ID
        this.updateById(entity);
        return entity.getOrgId();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteByIds(String ids) {
        List<String> orgs = StrUtil.split(ids, StrUtil.COMMA);
        for (String id : orgs) {
            // 校验是否存在
            validateOrgExists(id);
            // 校验是否有子部门
            if (this.baseMapper.selectCountByParentId(id) > 0) {
                throw exception(ORG_EXITS_CHILDREN, id);
            }
            // 删除部门
            this.baseMapper.deleteById(id);
        }
        return true;
    }

    /**
     * 机构 路径生成
     *
     * @param parentId 父ID
     * @return 父节点路径以英文逗号(, )分割，eg: 1,2,3
     */
    private String generateOrgTreePath(String parentId) {
        String treePath = null;
        if (ObjectUtil.equals(DDConstant.ROOT_NODE_ID_STR, parentId)) {
            treePath = String.valueOf(parentId);
        } else {
            SystemOrgDO parent = this.getById(parentId);
            if (parent != null) {
                treePath = parent.getParentIds() + StrUtil.COMMA + parent.getOrgId();
            }
        }
        return treePath;
    }

    @VisibleForTesting
    void validateOrgExists(String id) {
        if (id == null) {
            return;
        }
        SystemOrgDO orgDO = this.baseMapper.selectById(id);
        if (orgDO == null) {
            throw exception(ORG_NOT_FOUND);
        }
    }

    @VisibleForTesting
    void validateParentOrg(String id, String parentId) {
        if (parentId == null || ObjectUtil.equals(DDConstant.ROOT_NODE_ID_STR, parentId)) {
            return;
        }
        // 1. 不能设置自己为父部门
        if (Objects.equals(id, parentId)) {
            throw exception(ORG_PARENT_ERROR);
        }
        // 2. 父部门不存在
        SystemOrgDO parentDept = this.baseMapper.selectById(parentId);
        if (parentDept == null) {
            throw exception(ORG_PARENT_NOT_EXITS);
        }
        // 3. 递归校验父部门，如果父部门是自己的子部门，则报错，避免形成环路
        if (id == null) { // id 为空，说明新增，不需要考虑环路
            return;
        }
        for (int i = 0; i < Short.MAX_VALUE; i++) {
            // 3.1 校验环路
            parentId = parentDept.getParentId();
            if (Objects.equals(id, parentId)) {
                throw exception(ORG_PARENT_IS_CHILD);
            }
            // 3.2 继续递归下一级父部门
            if (parentId == null || ObjectUtil.equals(DDConstant.ROOT_NODE_ID_STR, parentId)) {
                break;
            }
            parentDept = this.baseMapper.selectById(parentId);
            if (parentDept == null) {
                break;
            }
        }
    }

    @VisibleForTesting
    void validateOrgNameUnique(String id, String parentId, String name) {
        SystemOrgDO orgDO = this.baseMapper.selectByParentIdAndName(parentId, name);
        if (orgDO == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的部门
        if (id == null) {
            throw exception(ORG_NAME_DUPLICATE);
        }
        if (ObjectUtil.notEqual(orgDO.getOrgId(), id)) {
            throw exception(ORG_NAME_DUPLICATE);
        }
    }
}
